#ifdef USE_LFS
#include <STM32FreeRTOS.h>
#include <core_debug.h>
#include <SPIFlash.h>
typedef unsigned int u32;
typedef unsigned char u8;
unsigned int GenerateCRC32Sum(const u8 *buf, unsigned int len, unsigned int init);

void saveRetain(void const *arg);
static osSemaphoreId r_xSemaphore = NULL;

class retain_flash
{
public:
    int begin(SPIFlash *flash, u32 retain_size, u32 adr1, u32 adr2, const u8 *hash, u32 hash_size, u8 thread = 1)
    {
        this->flash = flash;
        this->retain_size = retain_size;
        this->adr1 = adr1;
        this->adr2 = adr2;
        this->hash = hash;
        this->hash_size = hash_size;
        uint32_t i;
        if (retain_size > 0)
        {
            buf = (uint8_t *)malloc(retain_size);
        }

        /* Calc header offset.  */
        header_offset = sizeof(retain_size) + sizeof(hash_size) + hash_size;

        /*  Set header CRC initial state.  */
        header_crc = 0;

        /* Calc crc for header.  */
        header_crc = GenerateCRC32Sum(&retain_size, sizeof(retain_size), header_crc);

        header_crc = GenerateCRC32Sum(&hash_size, sizeof(hash_size), header_crc);

        header_crc = GenerateCRC32Sum(hash, hash_size, header_crc);
        if (!r_xSemaphore)
        {
            osSemaphoreDef(SEM);
            r_xSemaphore = osSemaphoreCreate(osSemaphore(SEM), 1);
        }
        if (thread)
        {
            osThreadDef(retain_thd, saveRetain, osPriorityLow, 0, 384); //256不够
            retain_thread = osThreadCreate(osThread(retain_thd), this);
        }
    }
    int close()
    {
        osThreadTerminate(retain_thread);
        osSemaphoreDelete(r_xSemaphore);
        r_xSemaphore = NULL;
        free(buf);
    }
    int check()
    {
        if (!retain_size)
            return 0;
        core_debug("retain size = %d", retain_size);
        /* Check latest retain file.  */
        if (CheckRetainFile(adr1))
            return 1;

        /* Check if we have backup.  */
        if (CheckRetainFile(adr2))
            return 1;
        core_debug("No valid retain buffer.");
        /* We don't have any valid retain buffer - nothing to remind.  */
        return 0;
    }
    int CheckRetainFile(u32 RETAIN_ADDR)
    {

        if (CheckFileCRC(RETAIN_ADDR))
            if (CheckFilehash(RETAIN_ADDR))
            {
                flash->readBytes(RETAIN_ADDR + sizeof(retain_size) + sizeof(hash_size) + hash_size, buf, retain_size);
                return 1;
            }
        return 0;
    }
    int CheckFileCRC(u32 RETAIN_ADDR)
    {
        /* Set the magic constant for one-pass CRC calc according to ZIP CRC32.  */
        const uint32_t magic_number = 0x2144df1c;

        /* CRC initial state.  */
        uint32_t calc_crc32 = 0;
        char data_block = 0;

        int adr = RETAIN_ADDR;
        while (adr < RETAIN_ADDR + sizeof(retain_size) + sizeof(hash_size) + hash_size + retain_size + sizeof(int))
        {
            data_block = flash->readByte(adr);
            adr++;
            calc_crc32 = GenerateCRC32Sum(&data_block, sizeof(char), calc_crc32);
        }

        /* Compare crc result with a magic number.  */
        return (calc_crc32 == magic_number) ? 1 : 0;
    }
    int CheckFilehash(u32 RETAIN_ADDR)
    {

        uint32_t k;
        int offset = sizeof(retain_size);
        uint32_t size;
        flash->readBytes(RETAIN_ADDR + offset, (uint8_t *)&size, sizeof(size));
        if (size != hash_size)
            return 0;
        for (k = 0; k < hash_size; k++)
        {
            uint8_t file_digit;
            file_digit = flash->readByte(RETAIN_ADDR + offset + sizeof(size) + k);
            if (file_digit != *(hash + k))
                return 0;
        }
        return 1;
    }
    void _saveRetain(int loop = 1)
    {
        if (loop)
            vTaskDelay(1000);
        last_crc = GenerateCRC32Sum(buf, retain_size, header_crc);

        while (1)
        {
            curr_crc = GenerateCRC32Sum(buf, retain_size, header_crc);
            if ((curr_crc != last_crc) || (!loop))
            {
                if (osSemaphoreWait(r_xSemaphore, portMAX_DELAY) == osOK)
                {
                    int adr = adr1;
                    flash->blockErase64K(adr);
                    vTaskDelay(10);
                    flash->writeBytes(adr, (const uint8_t *)&retain_size, sizeof(retain_size));
                    adr += sizeof(retain_size);
                    flash->writeBytes(adr, (const uint8_t *)&hash_size, sizeof(hash_size));
                    adr += sizeof(hash_size);
                    flash->writeBytes(adr, (const uint8_t *)hash, hash_size);
                    adr += hash_size;
                    flash->writeBytes(adr, (const uint8_t *)buf, retain_size);
                    adr += retain_size;
                    curr_crc = GenerateCRC32Sum(buf, retain_size, header_crc);
                    flash->writeBytes(adr, (const uint8_t *)&curr_crc, sizeof(uint32_t));
                    adr = adr2;
                    flash->blockErase64K(adr);
                    vTaskDelay(10);
                    flash->writeBytes(adr, (const uint8_t *)&retain_size, sizeof(retain_size));
                    adr += sizeof(retain_size);
                    flash->writeBytes(adr, (const uint8_t *)&hash_size, sizeof(hash_size));
                    adr += sizeof(hash_size);
                    flash->writeBytes(adr, (const uint8_t *)hash, hash_size);
                    adr += hash_size;
                    flash->writeBytes(adr, (const uint8_t *)buf, retain_size);
                    adr += retain_size;
                    curr_crc = GenerateCRC32Sum(buf, retain_size, header_crc);
                    flash->writeBytes(adr, (const uint8_t *)&curr_crc, sizeof(uint32_t));
                    last_crc = curr_crc;
                    osSemaphoreRelease(r_xSemaphore);
                }
            }
            if (loop)
                vTaskDelay(5000);
            else
                break;
        }
    }
    void reset()
    {
        flash->blockErase64K(adr1);
        flash->blockErase64K(adr2);
    }
    void remind(unsigned int offset, unsigned int count, void *p)
    {
        memcpy(p, buf + offset, count);
    }
    void retain(unsigned int offset, unsigned int count, void *p)
    {
        if (memcmp(buf + offset, p, count) != 0)
        {
            memcpy(buf + offset, p, count);
        }
    }

private:
    SPIFlash *flash;
    u8 current;
    u32 adr1, adr2;
    u32 retain_size;
    u32 hash_size;
    const u8 *hash;
    u32 header_offset;
    u32 header_crc;
    u8 *buf;
    osThreadId retain_thread;
    uint32_t last_crc;
    uint32_t curr_crc;

    static void saveRetain(void const *arg)
    {
        auto *rtn = (retain_flash *)arg;
        rtn->_saveRetain();
    }
};
#endif